/*
 Copyright 2009 Abdulla Abdurakhmanov (abdulla@latestbit.com)
 Original sources are available at www.latestbit.com

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.

 StdNetworkCommand.cpp. Created on: 08.09.2009 13:22:51
 */

#include "smartobjects/coders/StdNetworkCommand.hpp"

namespace smartobjects {

const unsigned char StdNetworkCommand::PROTOCOL_ID 				= 0xAA;
const unsigned char StdNetworkCommand::PROTOCOL_VERSION 		= 0x10;
const unsigned int StdNetworkCommand::COMMAND_SIZE_POSITION		= 8;
const unsigned int StdNetworkCommand::GRAMMAR_SIZE_POSITION		= 12;
const unsigned int StdNetworkCommand::HEADER_SIZE				= 16;

StdNetworkCommand::StdNetworkCommand(
		unsigned char nCommandId,
		ByteBuffer* poToBuffer,
		IDataTypesCoder* poCoder) :
			m_nProtocolVersion(PROTOCOL_VERSION),
			m_nCommandId ( nCommandId ),
			m_nMessageId ( 0 ),
			m_nSectionsFlags ( 0 ),
			m_nCommandBodySize ( 0 ),
			m_poBuffer ( poToBuffer ),
			m_poCoder ( poCoder ),
			m_bIsSerialized ( false ), m_bIsDeserialized ( false ),
			m_poRequestContext(NULL) {
	m_poBuffer->addRef();
	m_poCoder->addRef();
}

StdNetworkCommand::StdNetworkCommand(
		ByteBuffer* poFromBuffer,
		IDataTypesCoder* poCoder) :
			m_nProtocolVersion(PROTOCOL_VERSION),
			m_nCommandId ( 0 ),
			m_nMessageId ( 0 ),
			m_nSectionsFlags ( 0 ),
			m_nCommandBodySize ( 0 ),
			m_poBuffer ( poFromBuffer ),
			m_poCoder ( poCoder ),
			m_bIsSerialized ( false ), m_bIsDeserialized ( false ),
			m_poRequestContext(NULL) {
	m_poBuffer->addRef();
	m_poCoder->addRef();
}

StdNetworkCommand::StdNetworkCommand(
		const INetworkCommand* poBaseCommand ) :
			m_nProtocolVersion(PROTOCOL_VERSION),
			m_nCommandId ( 0 ),
			m_nMessageId ( 0 ),
			m_nSectionsFlags ( 0 ),
			m_nCommandBodySize ( 0 ),
			m_poCoder ( poBaseCommand->getDataTypesCoder() ),
			m_bIsSerialized ( false ), m_bIsDeserialized ( false ),
			m_poRequestContext(NULL) {
	m_poBuffer = poBaseCommand->getBuffer();
	m_poBuffer->addRef();
	m_nCommandId = poBaseCommand->getCommandId();
	m_sDestination = poBaseCommand->getDestination();
	m_sInstanceId = poBaseCommand->getInstanceId();
	m_nMessageId = poBaseCommand->getMessageId();
	m_nProtocolVersion = poBaseCommand->getProtocolVersion();
	m_poRequestContext = poBaseCommand->getRequestContext();
	if(m_poRequestContext)
		m_poRequestContext->addRef();
	m_poCoder->addRef();
}


StdNetworkCommand::~StdNetworkCommand() {
	releaseAndClear(m_poRequestContext);
	releaseAndClear(m_poBuffer);
	releaseAndClear(m_poCoder);
}

unsigned char StdNetworkCommand::getProtocolVersion() const {
	return m_nProtocolVersion;
}

unsigned char StdNetworkCommand::getCommandId() const {
	return m_nCommandId;
}

const std::string& StdNetworkCommand::getDestination() const {
	return m_sDestination;
}

void StdNetworkCommand::setDestination(const std::string& sValue) {
	m_sDestination = sValue;
}


const std::string& StdNetworkCommand::getInstanceId() const {
	return m_sInstanceId;
}

void StdNetworkCommand::setInstanceId(const std::string& sValue) {
	m_sInstanceId = sValue;
}

boost::uint32_t StdNetworkCommand::getMessageId() const {
	return m_nMessageId;
}

void StdNetworkCommand::setMessageId(boost::uint32_t nValue) {
	m_nMessageId = nValue;
}

unsigned int StdNetworkCommand::getHeaderSize() const {
	return HEADER_SIZE;
}

unsigned int StdNetworkCommand::getCommandBodySize() const {
	return m_nCommandBodySize;
}

RequestContext* StdNetworkCommand::getRequestContext() const {
	return m_poRequestContext;
}

void StdNetworkCommand::setRequestContext(RequestContext* poContext) {
	setNewRefObject(m_poRequestContext, poContext);
}

ByteBuffer* StdNetworkCommand::getBuffer() const {
	return m_poBuffer;
}

void StdNetworkCommand::setBuffer(ByteBuffer* poBuffer) {
	setNewRefObject(m_poBuffer, poBuffer);
}

bool StdNetworkCommand::isRequest() const {
	return m_nCommandId % 2 == 0;
}

bool StdNetworkCommand::isResponse() const {
	return !isRequest();
}

bool StdNetworkCommand::isSerialized() const {
	return m_bIsSerialized;
}

bool StdNetworkCommand::isDeserialized() const {
	return m_bIsDeserialized;
}

IDataTypesCoder* StdNetworkCommand::getDataTypesCoder() const {
	return m_poCoder;
}


void StdNetworkCommand::beginSerialize() {
	m_poCoder->reset();
	encodeHeader();
	if(isRequest()) {
		m_poCoder->encodeString(m_sDestination, m_poBuffer, true);
		m_poCoder->encodeString(m_sInstanceId, m_poBuffer, true);
		if(m_poRequestContext) {
			m_poRequestContext->serialize(m_poCoder, NULL, m_poBuffer);
		}
		else
			m_poCoder->encodeNull(m_poBuffer);
	}
}

void StdNetworkCommand::encodeHeader() {
	m_poBuffer->append(PROTOCOL_ID);
	m_poBuffer->append(PROTOCOL_VERSION);
	m_poBuffer->append(m_nCommandId);
	m_poBuffer->append(m_nSectionsFlags);

	appendIntFieldHeader( m_nMessageId );

	// Reserve for len
	static const boost::uint32_t nReserveForLen = 0;
	appendIntFieldHeader(nReserveForLen);

	// Reserve field for position of grammar
	appendIntFieldHeader(nReserveForLen);
}

void StdNetworkCommand::finishSerialize() {
	unsigned int nGrammarPosition = m_poBuffer->getLen() - HEADER_SIZE;
	replaceIntFieldHeader ( nGrammarPosition, GRAMMAR_SIZE_POSITION);
	m_poCoder->encodeGrammar(m_poBuffer);

	if(m_poBuffer->getLen()< HEADER_SIZE)
		throw DataTypesCoderException("Unable to finish serialization with body size less than header size");
	m_nCommandBodySize = m_poBuffer->getLen() - HEADER_SIZE;
	replaceIntFieldHeader ( m_nCommandBodySize, COMMAND_SIZE_POSITION);
	m_bIsSerialized = true;
}

void StdNetworkCommand::deserialize() {
	m_poCoder->reset();
	decodeHeader();

	if(isRequest()) {
		m_poCoder->decodeString(m_sDestination, m_poBuffer );
		m_poCoder->decodeString(m_sInstanceId, m_poBuffer);
		if(!m_poCoder->decodeNull(m_poBuffer)) {
			boost::intrusive_ptr<RequestContext> poContext ( new RequestContext(), false );
			setRequestContext( poContext.get() );
			m_poRequestContext->deserialize(m_poCoder, NULL, m_poBuffer);
		}
		else
			setRequestContext(NULL);
	}
	m_bIsDeserialized = true;
}

void StdNetworkCommand::decodeHeader() {
	if(m_poBuffer->getLenFromPos() < HEADER_SIZE)
		throw DataTypesCoderException("Incorrect protocol header size received!");

	unsigned char* nRcvIdent = m_poBuffer->getDataAtPos();

	if(*nRcvIdent!=PROTOCOL_ID) {
		throw DataTypesCoderException("Incorrect protocol header received!");
	}

	m_nProtocolVersion = *(++nRcvIdent);

	if( (m_nProtocolVersion & 0xF0) != (PROTOCOL_VERSION & 0xF0) )
		throw DataTypesCoderException("Unsupported protocol version detected!");

	m_nCommandId = *(++nRcvIdent);
	m_nSectionsFlags = *(++nRcvIdent);
	++nRcvIdent;
	m_nMessageId = decodeIntFieldHeader<boost::uint32_t>(&nRcvIdent);
	m_nCommandBodySize = decodeIntFieldHeader<boost::uint32_t>(&nRcvIdent);

	// Grammar
	unsigned int nGrammarPosition = decodeIntFieldHeader<boost::uint32_t>(&nRcvIdent);

	int saveCurrentPos = m_poBuffer->getPos();
	m_poBuffer->seek(nGrammarPosition + HEADER_SIZE);
	m_poCoder->decodeGrammar(m_poBuffer);
	m_poBuffer->setPos(saveCurrentPos);

	m_poBuffer->seek(HEADER_SIZE);
}

}
